   package mars;
   import mars.venus.*;
   import mars.assembler.*;
   import mars.simulator.*;
   import mars.mips.hardware.*;
   import java.util.*;
   import java.io.*;
   import java.awt.event.*;
   import javax.swing.*;

/*
Copyright (c) 2003-2006,  Pete Sanderson and Kenneth Vollmar

Developed by Pete Sanderson (psanderson@otterbein.edu)
and Kenneth Vollmar (kenvollmar@missouristate.edu)

Permission is hereby granted, free of charge, to any person obtaining 
a copy of this software and associated documentation files (the 
"Software"), to deal in the Software without restriction, including 
without limitation the rights to use, copy, modify, merge, publish, 
distribute, sublicense, and/or sell copies of the Software, and to 
permit persons to whom the Software is furnished to do so, subject 
to the following conditions:

The above copyright notice and this permission notice shall be 
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

(MIT license, http://www.opensource.org/licenses/mit-license.html)
 */

/**
 * Internal representations of MIPS program.  Connects source, tokens and machine code.  Having
 * all these structures available facilitates construction of good messages,
 * debugging, and easy simulation.
 * 
 * @author Pete Sanderson
 * @version August 2003
 **/

    public class MIPSprogram {
   
   // See explanation of method inSteppedExecution() below.
      private boolean steppedExecution = false;
   
      private String filename;
      private ArrayList sourceList;
      private ArrayList tokenList;
      private ArrayList parsedList;
      private ArrayList machineList;
      private BackStepper backStepper;
      private SymbolTable localSymbolTable;
   
   /**
    * Produces list of source statements that comprise the program.
    * @return ArrayList of String.  Each String is one line of MIPS source code.
    **/
    
       public ArrayList getSourceList() {
         return sourceList;
      }
   
   /**
    * Produces name of associated source code file.
    * @return File name as String. 
    **/
    
       public String getFilename() {
         return filename;
      }
   
   /**
    * Produces list of tokens that comprise the program.
    * @return ArrayList of TokenList.  Each TokenList is list of tokens generated by
    * corresponding line of MIPS source code.
    * @see TokenList
    **/
    
       public ArrayList getTokenList() {
         return tokenList;
      }
   
   /**
    * Produces new empty list to hold parsed source code statements.
    * @return ArrayList of ProgramStatement.  Each ProgramStatement represents a parsed
    * MIPS statement.
    * @see ProgramStatement
    **/
    
       public ArrayList createParsedList() {
         parsedList = new ArrayList();
         return parsedList;
      }
   
   /**
    * Produces existing list of parsed source code statements.
    * @return ArrayList of ProgramStatement.  Each ProgramStatement represents a parsed
    * MIPS statement.
    * @see ProgramStatement
    **/
    
       public ArrayList getParsedList() {
         return parsedList;
      }
   
   /**
    * Produces list of machine statements that are assembled from the program.
    * @return ArrayList of ProgramStatement.  Each ProgramStatement represents an assembled
    * basic MIPS instruction.
    * @see ProgramStatement
    **/
    
       public ArrayList getMachineList() {
         return machineList;
      }
   
   
   /**
    * Returns BackStepper associated with this program.  It is created upon successful assembly.
    * @return BackStepper object, null if there is none.
    **/
    
       public BackStepper getBackStepper() {
         return backStepper;
      }
   
   /**
    * Returns SymbolTable associated with this program.  It is created at assembly time,
    * and stores local labels (those not declared using .globl directive).
    **/
    
       public SymbolTable getLocalSymbolTable() {
         return localSymbolTable;
      }
   
   /**
    * Returns status of BackStepper associated with this program.  
    * @return true if enabled, false if disabled or non-existant.
    **/
    
       public boolean backSteppingEnabled() {
         return (backStepper!=null && backStepper.enabled());
      }
   
   /**
    * Produces specified line of MIPS source program.
    * @param i Line number of MIPS source program to get.  Line 1 is first line.
    * @return Returns specified line of MIPS source.  If outside the line range,
    * it returns null.  Line 1 is first line.
    **/
    
       public String getSourceLine(int i) {
         if ( (i >= 1) && (i <= sourceList.size()) )
            return (String) sourceList.get(i-1);
         else
            return null;
      }
   
   
   /**
    * Reads MIPS source code from file into structure.  Will always read from file.
    * It is GUI responsibility to assure that source edits are written to file
    * when user selects compile or run/step options.
    * 
    * @param file String containing name of MIPS source code file.
    * @throws ProcessingException Will throw exception if there is any problem reading the file.
    **/
   
       public void readSource(String file) throws ProcessingException {
         this.filename = file;
         this.sourceList = new ArrayList();
         ErrorList errors = null;
         BufferedReader inputFile;
         String line;
         int lengthSoFar = 0;
         try {
            inputFile = new BufferedReader(new FileReader(file));
            line = inputFile.readLine();
            while (line != null) {
               sourceList.add(line);
               line = inputFile.readLine();
            }
         } 
             catch (Exception e) {
               errors = new ErrorList();
               errors.add(new ErrorMessage(file,0,0,e.toString()));
               throw new ProcessingException(errors);
            }
         return;
      }
   
   /**
    * Tokenizes the MIPS source program. Program must have already been read from file.
    * @throws ProcessingException Will throw exception if errors occured while tokenizing.
    **/
   
       public void tokenize() throws ProcessingException {
         Tokenizer tokenizer = new Tokenizer();
         this.tokenList = tokenizer.tokenize(this);
         this.localSymbolTable = new SymbolTable(this.filename); // prepare for assembly
         return;
      }
   
   /**
    * Prepares the given list of files for assembly.  This involves
    * reading and tokenizing all the source files.  There may be only one.
    * @param filenames  ArrayList containing the source file name(s) in no particular order
    * @param leadFilename String containing name of source file that needs to go first and 
    * will be represented by "this" MIPSprogram object.
	 * @param exceptionHandler String containing name of source file containing exception
	 * handler.  This will be assembled first, even ahead of leadFilename, to allow it to
	 * include "startup" instructions loaded beginning at 0x00400000.  Specify null or
	 * empty String to indicate there is no such designated exception handler.
    * @return ArrayList containing one MIPSprogram object for each file to assemble.
    * objects for any additional files (send ArrayList to assembler)
    * @throws ProcessingException Will throw exception if errors occured while reading or tokenizing.
    **/
   
       public ArrayList prepareFilesForAssembly(ArrayList filenames, String leadFilename, String exceptionHandler) throws ProcessingException {
         ArrayList MIPSprogramsToAssemble = new ArrayList();
			int leadFilePosition = 0;
			if (exceptionHandler != null && exceptionHandler.length() > 0) {
			   filenames.add(0, exceptionHandler);
				leadFilePosition = 1;
			}
         for (int i=0; i<filenames.size(); i++) {
            String filename = (String) filenames.get(i);
            MIPSprogram preparee = (filename.equals(leadFilename)) ? this : new MIPSprogram();
            preparee.readSource(filename);
            preparee.tokenize();
         	// I want "this" MIPSprogram to be the first in the list...except for exception handler
            if (preparee == this && MIPSprogramsToAssemble.size()>0) {
               MIPSprogramsToAssemble.add(leadFilePosition,preparee);
            } 
            else {
               MIPSprogramsToAssemble.add(preparee);
            }
         }
         return MIPSprogramsToAssemble;
      }

   /**
    * Assembles the MIPS source program. All files comprising the program must have 
    * already been tokenized.  Assembler warnings are not considered errors.
    * @param MIPSprogramsToAssemble ArrayList of MIPSprogram objects, each representing a tokenized source file.
	 * @param extendedAssemblerEnabled A boolean value - true means extended (pseudo) instructions
	 * are permitted in source code and false means they are to be flagged as errors.
    * @throws ProcessingException Will throw exception if errors occured while assembling.
    * @return ErrorList containing nothing or only warnings (otherwise would have thrown exception).
    **/

       public ErrorList assemble(ArrayList MIPSprogramsToAssemble, boolean extendedAssemblerEnabled)
		        throws ProcessingException {   
			 return assemble(MIPSprogramsToAssemble, extendedAssemblerEnabled, false);
		 }
		  
   /**
    * Assembles the MIPS source program. All files comprising the program must have 
    * already been tokenized.
    * @param MIPSprogramsToAssemble ArrayList of MIPSprogram objects, each representing a tokenized source file.
	 * @param extendedAssemblerEnabled A boolean value - true means extended (pseudo) instructions
	 * are permitted in source code and false means they are to be flagged as errors
	 * @param warningsAreErrors A boolean value - true means assembler warnings will be considered errors and terminate
	   the assemble; false means the assembler will produce warning message but otherwise ignore warnings.
    * @throws ProcessingException Will throw exception if errors occured while assembling.
    * @return ErrorList containing nothing or only warnings (otherwise would have thrown exception).
    **/
    
       public ErrorList assemble(ArrayList MIPSprogramsToAssemble, boolean extendedAssemblerEnabled,
		        boolean warningsAreErrors) throws ProcessingException {
         this.backStepper = null;
         Assembler asm = new Assembler();
         this.machineList = asm.assemble(MIPSprogramsToAssemble, extendedAssemblerEnabled, warningsAreErrors);
         this.backStepper = new BackStepper();
         return asm.getErrorList();
      }
   
   
   /**
    * Simulates execution of the MIPS program. Program must have already been assembled.
    * Begins simulation at beginning of text segment and continues to completion.
    * @param breakPoints int array of breakpoints (PC addresses).  Can be null.
    * @return true if execution completed and false otherwise
    * @throws ProcessingException Will throw exception if errors occured while simulating.
    **/
    
       public boolean simulate(int[] breakPoints) throws ProcessingException {
         return this.simulateFromPC(breakPoints, -1, null);
      }
   
   
   /**
    * Simulates execution of the MIPS program. Program must have already been assembled.
    * Begins simulation at beginning of text segment and continues to completion or
    * until the specified maximum number of steps are simulated.
    * @param maxSteps  maximum number of steps to simulate.
    * @return true if execution completed and false otherwise
    * @throws ProcessingException Will throw exception if errors occured while simulating.
    **/
    
       public boolean simulate(int maxSteps) throws ProcessingException {
         return this.simulateFromPC(null, maxSteps, null);
      }	
   
   /**
    * Simulates execution of the MIPS program. Program must have already been assembled.
    * Begins simulation at current program counter address and continues until stopped,
    * paused, maximum steps exceeded, or exception occurs.
    * @param breakPoints int array of breakpoints (PC addresses).  Can be null.
    * @param maxSteps maximum number of instruction executions.  Default -1 means no maximum.
    * @param a the GUI component responsible for this call (GO normally).  set to null if none.
    * @return true if execution completed and false otherwise
    * @throws ProcessingException Will throw exception if errors occured while simulating.
    **/	
       public boolean simulateFromPC(int[] breakPoints, int maxSteps, AbstractAction a) throws ProcessingException {
         steppedExecution = false;
         Simulator sim = Simulator.getInstance();
         return sim.simulate(this, RegisterFile.getProgramCounter(), maxSteps, breakPoints, a);
      }
   
   
   
   /**
    * Simulates execution of the MIPS program. Program must have already been assembled.
    * Begins simulation at current program counter address and executes one step.
    * @param a the GUI component responsible for this call (STEP normally). Set to null if none.
    * @return true if execution completed and false otherwise
    * @throws ProcessingException Will throw exception if errors occured while simulating.
    **/
       public boolean simulateStepAtPC(AbstractAction a) throws ProcessingException {
         steppedExecution = true;
         Simulator sim = Simulator.getInstance();
         boolean done = sim.simulate(this, RegisterFile.getProgramCounter(), 1, null,a);
         return done;
      }
   
   /** Will be true only while in process of simulating a program statement
   * in step mode (e.g. returning to GUI after each step).  This is used to
   * prevent spurious AccessNotices from being sent from Memory and Register
   * to observers at other times (e.g. while updating the data and register
   * displays, while assembling program's data segment, etc).
   */
       public boolean inSteppedExecution() {
         return steppedExecution;
      }
   }  // MIPSprogram
